////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.  All rights reserved.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Gadgeteer.Modules
{
    using Color = Gadgeteer.Color;

    using Microsoft.SPOT;
    using Microsoft.SPOT.Hardware;
    using Microsoft.SPOT.Presentation;
    using Microsoft.SPOT.Presentation.Controls;
    using Microsoft.SPOT.Presentation.Media;
    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.IO;
    using System.Threading;

    public partial class Module
    {
        /// <summary>
        /// Abstract class to provide common methods, properties, and events for a display device.
        /// </summary>
        public abstract class DisplayModule : Module
        {
            private SimpleGraphicsInterface _simpleGraphics = null;

            private Window _wpfWindow = null;

            /// <summary>
            /// When overridden in a derived class, renders display data on the display device.
            /// </summary>
            /// <param name="bitmap">The <see cref="T:Microsoft.SPOT.Bitmap"/> object to render on the display.</param>
            protected abstract void Paint(Bitmap bitmap);

            private WPFRenderOptions _wpfrenderoptions;

            /// <summary>
            /// Provides an enumeration of possible values for creating 
            /// the <see cref="WPFWindow"/> property.
            /// </summary>
            /// <remarks>
            /// A value from this enumeration is used internally by the Gadgeteer
            /// core library when creating a display device object.
            /// This allows non-natively-supported displays (e.g. SPI displays) to use the <see cref="WPFWindow"/>
            /// property.
            /// </remarks>
            protected enum WPFRenderOptions
            {
                /// <summary>
                /// Rendering is intercepted to send to non-LCD display (e.g. SPI displays)
                /// </summary>
                Intercept,
                /// <summary>
                /// Rendering is not intercepted, the LCD controller sends output to the display
                /// </summary>
                Ignore
            }

            // Note: A constructor summary is auto-generated by the doc builder.
            /// <summary></summary>
            /// <param name="wpfrenderoptions">
            /// A value from the <see cref="WPFRenderOptions"/> enumeration that specifies
            /// whether the WPF output from a <see cref="WPFWindow"/> is sent to the LCD controller or intercepted to send to an SPI display.
            /// </param>
            protected DisplayModule(WPFRenderOptions wpfrenderoptions)
            {
                _wpfrenderoptions = wpfrenderoptions;
                if (_numDisplaysConstructed != _numDisplaysConfigured)
                {
                    DisplayError("ERROR IN DISPLAY MODULE DRIVER: All display modules constructors must call DisplayModule.SetLCDConfig()");
                }
                _numDisplaysConstructed++;
                _displayModuleToBeConfigured = this;
            }
            private Mainboard.LCDConfiguration _config = null;

            private static int _numDisplaysConstructed = 0;
            private static int _numDisplaysConfigured = 0;

            private static bool _lcdConfigInUnknownState = false;
            private static DisplayModule _displayModuleToBeConfigured = null;
            private static Mainboard.LCDConfiguration _lcdControllerConfig = null;

            private static ExtendedWeakReference _lcdConfigurationEWR = null;

            [Serializable]
            private sealed class LCDConfigurationEWR
            {
                public bool LCDControllerEnabled;

                public LCDConfigurationEWR(bool LCDControllerEnabled)
                {
                    this.LCDControllerEnabled = LCDControllerEnabled;
                }
            }

            private static bool LCDControllerEnabled
            {
                get
                {
                    if (_lcdConfigurationEWR == null)
                    {
                        _lcdConfigurationEWR = ExtendedWeakReference.RecoverOrCreate(typeof(LCDConfigurationEWR), 0, ExtendedWeakReference.c_SurviveBoot | ExtendedWeakReference.c_SurvivePowerdown);
                        _lcdConfigurationEWR.Priority = (int)ExtendedWeakReference.PriorityLevel.Critical;
                        if (_lcdConfigurationEWR.Target == null)
                        {
                            _lcdConfigurationEWR.Target = new LCDConfigurationEWR(false);
                            _lcdConfigInUnknownState = true;
                            Debug.Print("Creating extended weak reference to store LCD configuration. (You should not see this message unless the mainboard is new or has its firmware updated.)");
                        }
                    }
                    return ((LCDConfigurationEWR)_lcdConfigurationEWR.Target).LCDControllerEnabled;
                }
                set
                {
                    if (value != LCDControllerEnabled)
                    {
                        _lcdConfigurationEWR.Target = new LCDConfigurationEWR(value);
                        DisplayReboot();
                    }
                }
            }

            private static void DisplayError(string message)
            {
                Debug.Print(message);
                while (true)
                {
                    Mainboard.SetDebugLED(true);
                    Thread.Sleep(200);
                    Mainboard.SetDebugLED(false);
                    Thread.Sleep(200);
                }
            }
            private static void DisplayReboot()
            {
                Debug.Print("Updating display configuration. THE MAINBOARD WILL NOW REBOOT.");
                Debug.Print("To continue debugging, you will need to restart debugging manually (Ctrl-Shift-F5)");
                Thread.Sleep(1000);
                PowerState.RebootDevice(false);
            }

            /// <summary>
            /// Gets the <see cref="T:Gadgeteer.Modules.SimpleGraphics"/> object for this display module.
            /// </summary>
            /// <remarks>
            /// The <see cref="T:Gadgeteer.Modules.SimpleGraphics"/> object enables you to display text and simple shapes. 
            /// If your application requires more complex display capabilities, you can access the display
            /// via the <see cref="WPFWindow"/> property.
            /// </remarks>
            public SimpleGraphicsInterface SimpleGraphics
            {
                get
                {
                    if (_simpleGraphics == null)
                    {
                        _simpleGraphics = new SimpleGraphicsInterface(this);
                    }

                    return _simpleGraphics;
                }
            }

            /// <summary>
            /// Gets the Windows Presentation Foundation (WPF) window associated with this display.
            /// </summary>
            /// <remarks>
            /// This property enables you to access a WPF window and perform more complex display layouts and tasks 
            /// such as adding a <see cref="T:Microsoft.SPOT.Presentation.Controls.StackPanel"/> or other WPF controls
            /// that are included in the Microsoft .NET Micro Framework.
            ///  When using this property, you must reference it at least once before creating any 
            ///  <see cref="T:Microsoft.SPOT.Presentation.UIElement"/> objects that you want to place in the window. 
            ///  Otherwise, the .NET Micro Framework will throw a <see cref="T:System.NullReferenceException"/>.
            /// </remarks>
            public Window WPFWindow
            {
                get
                {
                    if (_config == null)
                    {
                        DisplayError("ERROR IN DISPLAY MODULE DRIVER: All display module constructors must call DisplayModule.SetLCDConfig()");
                    }
                    if (_lcdControllerConfig == null) 
                    {
                        // set LCD controller settings to use this configuration so we can use WPF (which relies on the LCD controller being configured)
                        if (_config.LCDControllerEnabled) DisplayError("ERROR: WPF should not be used before the ProgramStarted event"); // _lcdControllerConfig should not have been null
                        Mainboard.SetLCD(_config);
                        LCDControllerEnabled = _config.LCDControllerEnabled; // this is always false (since LCDs are configured at SetLCDConfig time)
                        if (SystemMetrics.ScreenWidth != _config.Width || SystemMetrics.ScreenHeight != _config.Height) DisplayReboot();
                        _lcdControllerConfig = _config;
                    }
                    if (_lcdControllerConfig != _config)
                    {
                        if(_lcdControllerConfig.LCDControllerEnabled) 
                        {
                            DisplayError("ERROR: If the onboard LCD controller (sockets R, G, B) are used, you can only use WPF with the display on those sockets.");
                        } 
                        else
                        {
                            DisplayError("ERROR: You can only use WPF on one display (and if an LCD is present, only on the LCD)");       
                        }
                        return null;
                    }

                    if (_wpfWindow == null)
                    {
                        if (SystemMetrics.ScreenWidth != _config.Width || SystemMetrics.ScreenHeight != _config.Height) DisplayError("System reboot should have occurred to set width/height in SystemMetrics");
                        _wpfWindow = new Window();
                        _wpfWindow.Height = (int) _config.Height;
                        _wpfWindow.Width = (int) _config.Width; 
                        _wpfWindow.Visibility = Visibility.Visible;

                        if (_wpfrenderoptions == WPFRenderOptions.Intercept) WindowManager.Instance.PostRender += new PostRenderEventHandler(Instance_PostRender);
                    }
                    return _wpfWindow;
                }
            }

            private delegate void voiddelegate(); // WORKAROUND
            private voiddelegate invalidate; // WORKAROUND
            private void Instance_PostRender(DrawingContext dc)
            {
                
                // The below workaround handles the issue that the OLED screen driver API cannot handle partial screen updates
                // WORKAROUND - when the clipping rectangle is not the full size, invalidate the whole window so we get called again with the full size
                int x, y, width, height;
                dc.GetClippingRectangle(out x, out y, out width, out height);
                if (x != 0 || y != 0 || width != dc.Width || height != dc.Height)
                {
                    //Debug.Print("Paint rectangle: x " + x + "  y " + y + " w " + width + " h " + height);
                    if (invalidate == null) invalidate = new voiddelegate(WPFWindow.Invalidate);  // WORKAROUND
                    Program.BeginInvoke(invalidate, null); // WORKAROUND
                    return; // WORKAROUND
                }
             
                Paint(dc.Bitmap);
            }

            /// <summary>
            /// When overridden in a derived class, gets the width of the display module.
            /// </summary>
            public abstract uint Width
            {
                get;
            }

            /// <summary>
            /// When overridden in a derived class, gets the height of the display module.
            /// </summary>
            public abstract uint Height
            {
                get;
            }

            /// <summary>
            /// The default configuration of the LCD controller when pins are used as IOs.  NB using 128x128 since that's an oft-used OLED display resolution, and avoids a reboot if an OLED is used.
            /// </summary>
            private static Gadgeteer.Mainboard.LCDConfiguration lcdPinsUsedAsIOs = new Mainboard.LCDConfiguration() { LCDControllerEnabled = false, Height = 128, Width = 128 };

            /// <summary>
            /// Called when a pin otherwise used for the LCD controller is reserved for another module
            /// We have to ensure the LCD controller pins are disabled so they can be reused
            /// </summary>
            internal static void LCDControllerPinReuse()
            {
                if (_lcdControllerConfig != null && _lcdControllerConfig.LCDControllerEnabled)
                {
                    DisplayError("ERROR: Cannot use LCD controller pins as IOs simultaneously with using them to drive a display");
                }

                if (_lcdConfigInUnknownState || LCDControllerEnabled)
                {
                    Mainboard.SetLCD(lcdPinsUsedAsIOs);
                    LCDControllerEnabled = false;
                    // need to explicitly reboot in case _lcdConfigInUnknownState
                     DisplayReboot();
                }
                if(_lcdControllerConfig == null) _lcdControllerConfig = lcdPinsUsedAsIOs;
            }

            internal static void CheckDisplayConfig()
            {
                if (_numDisplaysConstructed != _numDisplaysConfigured)
                {
                    DisplayError("ERROR IN DISPLAY MODULE DRIVER: All display module constructors must call DisplayModule.SetLCDConfig()");
                }
            }

            /// <summary>
            /// Sets the display configuration.  This must be called by all display module constructors (even ones not using LCD displays)
            /// </summary>
            /// <param name="displayConfig">The display configuration.</param>
            protected static void SetLCDConfig(Mainboard.LCDConfiguration displayConfig)
            {
                if (_numDisplaysConstructed != _numDisplaysConfigured + 1)
                {
                    DisplayError("ERROR IN DISPLAY MODULE DRIVER: All display module constructors must call DisplayModule.SetLCDConfig()");
                }

                if (displayConfig != Mainboard.LCDConfiguration.HeadlessConfig && (displayConfig.Height == 0 || displayConfig.Width == 0))
                {
                    DisplayError("ERROR IN DISPLAY MODULE DRIVER: LCDConfiguration must specify Height and Width of display");
                }
                _numDisplaysConfigured++;
                _displayModuleToBeConfigured._config = displayConfig;

                if (displayConfig.LCDControllerEnabled)
                {
                    if (_lcdControllerConfig != null)
                    {
                        if (_lcdControllerConfig == lcdPinsUsedAsIOs)
                        {
                            DisplayError("ERROR: Cannot use LCD controller pins as IOs and also use an LCD controller based display");
                        }
                        else
                        {
                            DisplayError("ERROR: Cannot use more than one LCD controller based display at a time");
                        }
                    }

                    _lcdControllerConfig = displayConfig;
                    Mainboard.SetLCD(displayConfig);
                    // this reboots if necessary
                    LCDControllerEnabled = true;
                    // known issue: this does not check all settings match (will be fixed in the NETMF 4.2 version)
                    if (SystemMetrics.ScreenWidth != displayConfig.Width || SystemMetrics.ScreenHeight != displayConfig.Height) DisplayReboot();
                }
            }


            /// <summary>
            /// Provides access to display tasks such as displaying text, simple shapes, and images.
            /// </summary>
            /// <remarks>
            /// <para>
            ///  A <see cref="SimpleGraphics"/> object enables you to perform simple display tasks
            ///  such as displaying text, simple shapes, and images.
            /// </para>
            /// <para>
            ///  You cannot create a <see cref="SimpleGraphics"/> object directly. Access this object 
            ///  via the <see cref="P:Gadgeteer.Modules.DisplayModule.SimpleGraphics"/> property
            ///  of a <see cref="T:Gadgeteer.Modules.DisplayModule"/> object.
            /// </para>
            /// <para>
            ///  If your application requires more complex layout or display tasks, consider using
            ///  the <see cref="P:Gadgeteer.Modules.DisplayModule.WPFWindow"/> property, instead.
            /// </para>
            /// </remarks>
            public class SimpleGraphicsInterface
            {
                private DisplayModule _renderer;

                internal SimpleGraphicsInterface(DisplayModule renderer)
                {
                    AutoRedraw = true;

                    _backgroundColor = Color.Black;

                    _display = new Bitmap((int)renderer.Width, (int)renderer.Height);

                    _renderer = renderer;

                    Clear();
                }

                private Bitmap _display;

                private Color _backgroundColor;

                /// <summary>
                /// Gets or sets a value that indicates whether the display should automatcially refresh
                /// after it has been issued a drawing command.
                /// </summary>
                /// <remarks>
                /// Set this property to <b>false</b> and use the <see cref="Redraw"/> method to improve
                /// performance when displaying mutiple shapes or lines of text at the same time. When 
                /// <see cref="AutoRedraw"/> is <b>false</b>, display operations do not render until
                /// you call the <see cref="Redraw"/> method. 
                /// </remarks>
                public bool AutoRedraw { get; set; }

                /// <summary>
                /// Gets the height of this display.
                /// </summary>
                public uint Height { get { return _renderer.Height; } }

                /// <summary>
                /// Gets the width of this display.
                /// </summary>
                public uint Width { get { return _renderer.Width; } }

                /// <summary>
                /// Redraws the display.
                /// </summary>
                /// <remarks>
                /// Use this method in conjunction with the <see cref="AutoRedraw"/> property to improve
                /// performance when displaying mutiple shapes or lines of text at the same time. When 
                /// <see cref="AutoRedraw"/> is <b>false</b>, display operations do not render until
                /// you call this method. 
                /// </remarks>
                public void Redraw()
                {
                    _renderer.Paint(_display);
                }

                /// <summary>
                /// Clears the display, but does not redraw it.
                /// </summary>
                /// <seealso cref="Redraw"/>
                /// <seealso cref="AutoRedraw"/>
                public void ClearNoRedraw()
                {
                    _display.DrawRectangle(_backgroundColor, 0, 0, 0, (int)this.Width, (int)this.Height, 0, 0, _backgroundColor, 0, 0, _backgroundColor, 0, 0, Bitmap.OpacityOpaque);
                }

                /// <summary>
                /// Clears the display and (if <see cref="AutoRedraw"/> is <b>true</b>), redraws it.
                /// </summary>
                /// <seealso cref="ClearNoRedraw"/>
                /// <seealso cref="Redraw"/>
                /// <seealso cref="AutoRedraw"/>
                public void Clear()
                {
                    //            _display.Clear(); // this is superfluous with the below?
                    _display.DrawRectangle(_backgroundColor, 0, 0, 0, (int)this.Width, (int)this.Height, 0, 0, _backgroundColor, 0, 0, _backgroundColor, 0, 0, Bitmap.OpacityOpaque);

                    if (AutoRedraw)
                    {
                        Redraw();
                    }
                }

                /// <summary>
                /// Gets or sets the background color for the display.
                /// </summary>
                public Color BackgroundColor
                {
                    get
                    {
                        return _backgroundColor;
                    }

                    set
                    {
                        _backgroundColor = value;

                        if (AutoRedraw) Clear();
                    }
                }

                /// <summary>
                /// Displays the specified text.
                /// </summary>
                /// <param name="text">The text to display.</param>
                /// <param name="font">The font to use for the text display.</param>
                /// <param name="color">The color of the text.</param>
                /// <param name="x">The X coordinate to begin the text display.</param>
                /// <param name="y">The Y coordinate to begin the text display.</param>
                /// <remarks>
                /// This method displays text at the specified screen location. 
                /// If the text is too long for the display, it will be clipped.
                /// </remarks>
                public void DisplayText(string text, Font font, Color color, uint x, uint y)
                {
                    _display.DrawText(text, font, color, (int)x, (int)y);

                    if (AutoRedraw) Redraw();
                }

                /// <summary>
                /// Provides an enumeration that specifies how text will be aligned.
                /// </summary>
                /// <remarks>
                /// The values of this enumeration are used when calling 
                /// the <see cref="DisplayTextInRectangle(string, uint, uint, uint, uint, Color, Font, TextAlign, WordWrap, Trimming, ScaleText)">DisplayTextInRectangle</see> method.
                /// </remarks>
                public enum TextAlign
                {
                    /// <summary>
                    /// The text is aligned in the center of the specified rectangular region.
                    /// </summary>
                    Center,
                    /// <summary>
                    /// The text is aligned on the left of the specified rectangular region.
                    /// </summary>
                    Left,
                    /// <summary>
                    /// The text is aligned on the right of the specified rectangular region.
                    /// </summary>
                    Right
                }

                /// <summary>
                /// Provides an enumeration that specifies how text will be trimmed.
                /// </summary>
                /// <remarks>
                /// The values of this enumeration are used when calling 
                /// the <see cref="DisplayTextInRectangle(string, uint, uint, uint, uint, Color, Font, TextAlign, WordWrap, Trimming, ScaleText)">DisplayTextInRectangle</see> method.
                /// </remarks>
                public enum Trimming
                {
                    /// <summary>
                    /// Trimming occurs a word boundary, followed by an ellipsis mark.
                    /// </summary>
                    WordEllipsis,
                    /// <summary>
                    /// Trimming occurs a character boundary, followed by an ellipsis mark.
                    /// </summary>
                    CharacterEllipsis
                }

                /// <summary>
                /// Provides an enumeration that specifies how text will be wrapped.
                /// </summary>
                /// <remarks>
                /// The values of this enumeration are used when calling 
                /// the <see cref="DisplayTextInRectangle(string, uint, uint, uint, uint, Color, Font, TextAlign, WordWrap, Trimming, ScaleText)">DisplayTextInRectangle</see> method.
                /// </remarks>
                public enum WordWrap
                {
                    /// <summary>
                    /// Do not use word wrap.
                    /// </summary>
                    None,
                    /// <summary>
                    /// Use word wrap.
                    /// </summary>
                    Wrap
                }

                /// <summary>
                /// Provides an enumeration that specifies how text will be scaled.
                /// </summary>
                /// <remarks>
                /// The values of this enumeration are used when calling 
                /// the <see cref="DisplayTextInRectangle(string, uint, uint, uint, uint, Color, Font, TextAlign, WordWrap, Trimming, ScaleText)">DisplayTextInRectangle</see> method.
                /// </remarks>
                public enum ScaleText
                {
                    /// <summary>
                    /// If neccessary, the height of the rectangular region will be expanded to accomodate the text.
                    /// </summary>
                    None,
                    /// <summary>
                    /// The height of the rectangular region will not be expanded to accomodate text that is too long.
                    /// In this case, the value of the <see cref="Trimming"/> enumeration determines how the excess text
                    /// is treated.
                    /// </summary>
                    ScaleToFit
                }

                /// <summary>
                /// Displays the specified text within the specified rectangular region.
                /// </summary>
                /// <param name="text">The text to display.</param>
                /// <param name="x">The X coordinate of the rectangular region.</param>
                /// <param name="y">The Y coordinate of the rectangular region.</param>
                /// <param name="width">The width of the rectangular region.</param>
                /// <param name="height">The height of the rectangular region.</param>
                /// <param name="color">The text color.</param>
                /// <param name="font">The text font.</param>
                /// <param name="textAlignment">A value from the <see cref="TextAlign"/> enumeration that specifies how to align the text within the rectangular region.</param>
                /// <param name="wordWrap">A value from the <see cref="WordWrap"/> enumeration that specifies how to wrap the text within the rectangular region.</param>
                /// <param name="trimming">A value from the <see cref="Trimming"/> enumeration that specifies how to trim excess text.</param>
                /// <param name="scaleTextToFit">A value from the <see cref="ScaleText"/> enumeration that specifies how the text should be scaled.</param>
                public void DisplayTextInRectangle(string text, uint x, uint y, uint width, uint height,
                    Color color, Font font, TextAlign textAlignment, WordWrap wordWrap, Trimming trimming, ScaleText scaleTextToFit)
                {
                    uint dtFlags = Bitmap.DT_None;

                    switch (textAlignment)
                    {
                        case TextAlign.Center:
                            dtFlags |= Bitmap.DT_AlignmentCenter;
                            break;
                        case TextAlign.Left:
                            dtFlags |= Bitmap.DT_AlignmentLeft;
                            break;
                        case TextAlign.Right:
                            dtFlags |= Bitmap.DT_AlignmentRight;
                            break;
                        default:
                            break;
                    }

                    switch (trimming)
                    {
                        case Trimming.CharacterEllipsis:
                            dtFlags |= Bitmap.DT_TrimmingCharacterEllipsis;
                            break;
                        case Trimming.WordEllipsis:
                            dtFlags |= Bitmap.DT_TrimmingWordEllipsis;
                            break;
                        default:
                            break;
                    }


                    if (wordWrap == WordWrap.Wrap)
                        dtFlags |= Bitmap.DT_WordWrap;

                    if (scaleTextToFit == ScaleText.None)
                        dtFlags |= Bitmap.DT_IgnoreHeight;

                    _display.DrawTextInRect(text, (int)x, (int)y, (int)width, (int)height, dtFlags, color, font);

                    if (AutoRedraw) Redraw();
                }

                /// <summary>
                /// Displays the specified text within the specified rectangular region.
                /// </summary>
                /// <param name="text">The text to display.</param>
                /// <param name="x">The X coordinate of the rectangular region.</param>
                /// <param name="y">The Y coordinate of the rectangular region.</param>
                /// <param name="width">The width of the rectangular region.</param>
                /// <param name="height">The height of the rectangular region.</param>
                /// <param name="color">The text color.</param>
                /// <param name="font">The text font.</param>
                /// <param name="textAlignment">A value from the <see cref="TextAlign"/> enumeration that specifies how to align the text within the rectangular region.</param>
                public void DisplayTextInRectangle(string text, uint x, uint y, uint width, uint height, Color color, Font font, TextAlign textAlignment)
                {
                    DisplayTextInRectangle(text, x, y, width, height, color, font, textAlignment, WordWrap.None, Trimming.WordEllipsis, ScaleText.None);
                }

                /// <summary>
                /// Displays the specified text within the specified rectangular region.
                /// </summary>
                /// <param name="text">The text to display.</param>
                /// <param name="x">The X coordinate of the rectangular region.</param>
                /// <param name="y">The Y coordinate of the rectangular region.</param>
                /// <param name="width">The width of the rectangular region.</param>
                /// <param name="height">The height of the rectangular region.</param>
                /// <param name="color">The text color.</param>
                /// <param name="font">The text font.</param>
                public void DisplayTextInRectangle(string text, uint x, uint y, uint width, uint height, Color color, Font font)
                {
                    DisplayTextInRectangle(text, x, y, width, height, color, font, TextAlign.Left);
                }

                /// <summary>
                /// Displays an ellipse.
                /// </summary>
                /// <param name="outlineColor">The color of the ellipse outline.</param>
                /// <param name="x">The X coordinate of the center of the ellipse.</param>
                /// <param name="y">The Y coordinate of the center of the ellipse.</param>
                /// <param name="xRadius">The radius value for Y.</param>
                /// <param name="yRadius">The radius value for X.</param>
                public void DisplayEllipse(Color outlineColor, uint x, uint y, uint xRadius, uint yRadius)
                {
                    _display.DrawEllipse(outlineColor, (int)x, (int)y, (int)xRadius, (int)yRadius);

                    if (AutoRedraw) Redraw();
                }

                /// <summary>
                /// Displays a rectangle.
                /// </summary>
                /// <param name="outlineColor">The color for the outline of the rectangle.</param>
                /// <param name="thicknessOutline">The thickness of the outline.</param>
                /// <param name="x">The X coordinate for the top left corner of the rectangle.</param>
                /// <param name="y">The Y coordinate for the top left corner of the rectangle.</param>
                /// <param name="width">The width of the rectangle.</param>
                /// <param name="height">The height of the rectangle.</param>
                /// <param name="xCornerRadius">The X dimension corner radius, or zero for none.</param>
                /// <param name="yCornerRadius">The Y dimension corner radius, or zero for none.</param>
                /// <param name="colorGradientStart">The color to begin the background gradient.</param>
                /// <param name="xGradientStart">The X coordinate to begin the background gradient.</param>
                /// <param name="yGradientStart">The Y coordinate to begin the background gradient.</param>
                /// <param name="colorGradientEnd">The color to end the background gradient.</param>
                /// <param name="xGradientEnd">The X coordinate to end the background gradient.</param>
                /// <param name="yGradientEnd">The Y coordinate to end the background gradient.</param>
                /// <param name="opacity">The opacity of the rectangle, 0 (transparent)..256 (opaque).</param>
                public void DisplayRectangle(Color outlineColor, uint thicknessOutline, uint x, uint y, uint width, uint height,
                    uint xCornerRadius, uint yCornerRadius, Color colorGradientStart, uint xGradientStart, uint yGradientStart,
                    Color colorGradientEnd, uint xGradientEnd, uint yGradientEnd, ushort opacity)
                {
                    _display.DrawRectangle(outlineColor, (int)thicknessOutline, (int)x, (int)y, (int)width, (int)height, (int)xCornerRadius, (int)yCornerRadius,
                        colorGradientStart, (int)xGradientStart, (int)yGradientStart, colorGradientEnd, (int)xGradientEnd, (int)yGradientEnd, opacity);

                    if (AutoRedraw) Redraw();
                }

                /// <summary>
                /// Displays a rectangle.
                /// </summary>
                /// <param name="outlineColor">The color for the outline of the rectangle.</param>
                /// <param name="thicknessOutline">The thickness of the outline.</param>
                /// <param name="fillColor">The color to fill the rectangle with.</param>
                /// <param name="x">The X coordinate for the top left corner of the rectangle.</param>
                /// <param name="y">The Y coordinate for the top left corner of the rectangle.</param>
                /// <param name="width">The width of the rectangle.</param>
                /// <param name="height">The height of the rectangle.</param>
                public void DisplayRectangle(Color outlineColor, uint thicknessOutline, Color fillColor, uint x, uint y, uint width, uint height)
                {
                    DisplayRectangle(outlineColor, thicknessOutline, x, y, width, height, 0, 0, fillColor, 0, 0, fillColor, 0, 0, Bitmap.OpacityOpaque);
                }

                /// <summary>
                /// Displays a rectangle.
                /// </summary>
                /// <param name="outlineColor">The color for the outline of the rectangle.</param>
                /// <param name="thicknessOutline">The thickness of the outline.</param>
                /// <param name="fillColor">The color to fill the rectangle with.</param>
                /// <param name="x">The X coordinate for the top left corner of the rectangle.</param>
                /// <param name="y">The Y coordinate for the top left corner of the rectangle.</param>
                /// <param name="width">The width of the rectangle.</param>
                /// <param name="height">The height of the rectangle.</param>
                /// <param name="opacity">The opacity of the rectangle, 0 (transparent)..256 (opaque).</param>
                public void DisplayRectangle(Color outlineColor, uint thicknessOutline, Color fillColor, uint x, uint y, uint width, uint height, ushort opacity)
                {
                    DisplayRectangle(outlineColor, thicknessOutline, x, y, width, height, 0, 0, fillColor, 0, 0, fillColor, 0, 0, opacity);
                }

                /// <summary>
                /// Displays a rectangle.
                /// </summary>
                /// <param name="outlineColor">The color for the outline of the rectangle.</param>
                /// <param name="thicknessOutline">The thickness of the outline.</param>
                /// <param name="fillColor">The color to fill the rectangle with.</param>
                /// <param name="x">The X coordinate for the top left corner of the rectangle.</param>
                /// <param name="y">The Y coordinate for the top left corner of the rectangle.</param>
                /// <param name="width">The width of the rectangle.</param>
                /// <param name="height">The height of the rectangle.</param>
                /// <param name="cornerRadius">The corner radius to be applied in both the X and Y dimensions.</param>
                /// <param name="opacity">The opacity of the rectangle, 0 (transparent)..256 (opaque).</param>
                public void DisplayRectangle(Color outlineColor, uint thicknessOutline, Color fillColor, uint x, uint y, uint width, uint height, uint cornerRadius, ushort opacity)
                {
                    DisplayRectangle(outlineColor, thicknessOutline, x, y, width, height, cornerRadius, cornerRadius, fillColor, 0, 0, fillColor, 0, 0, opacity);
                }

                /// <summary>
                /// Sets a specified pixel to the specified color.
                /// </summary>
                /// <param name="color">The color to set the pixel to.</param>
                /// <param name="x">The X coordinate of the pixel.</param>
                /// <param name="y">The Y coordinate of the pixel.</param>
                public void SetPixel(Color color, uint x, uint y)
                {
                    _display.SetPixel((int)x, (int)y, color);
                    if (AutoRedraw) Redraw();
                }

                /// <summary>
                /// Displays a <see cref="T:Microsoft.SPOT.Bitmap"/> image on the screen.
                /// </summary>
                /// <param name="bitmap">The <see cref="Microsoft.SPOT.Bitmap"/> object to display.</param>
                /// <param name="x">Horizontal position of the left edge of the bitmap on the display.</param>
                /// <param name="y">Vertical position of the top edge of the bitmap on the display.</param>
                /// <param name="xSrc">Source X coordinate. Use this parameter to specify cropping from the left edge of the image. Use 0 to display full image.</param>
                /// <param name="ySrc">Source Y coordinate. Use this parameter to specify cropping from the top edge of the image. Use 0 to display full image.</param>
                /// <param name="width">Source width. Use this parameter to specify cropping to the right of the image. Use bitmap.Width to display full image.</param>
                /// <param name="height">Source height. Use this parameter to specify cropping to the right of the image. Use bitmap.Height to display full image.</param>
                public void DisplayImage(Bitmap bitmap, uint x, uint y, uint xSrc, uint ySrc, uint width, uint height)
                {
                    _display.DrawImage((int)x, (int)y, bitmap, (int)xSrc, (int)ySrc, (int)width, (int)height);

                    if (AutoRedraw) Redraw();
                }

                /// <summary>
                /// Displays a <see cref="T:Microsoft.SPOT.Bitmap"/> image on the screen.
                /// </summary>
                /// <param name="bitmap">The <see cref="Microsoft.SPOT.Bitmap"/> object to display</param>
                /// <param name="x">Horizontal position of the left edge of the bitmap on the display.</param>
                /// <param name="y">Vertical position of the top edge of the bitmap on the display.</param>   
                public void DisplayImage(Bitmap bitmap, uint x, uint y)
                {
                    DisplayImage(bitmap, x, y, 0, 0, (uint)bitmap.Width, (uint)bitmap.Height);
                }

                /// <summary>
                /// Displays a <see cref="T:Gadgeteer.Picture"/> image on the screen.
                /// </summary>
                /// <param name="picture">The <see cref="T:Gadgeteer.Picture"/> to display.</param>
                /// <param name="x">Horizontal position of the left edge of the image on the display.</param>
                /// <param name="y">Vertical position of the top edge of the image on the display.</param>
                /// <param name="xSrc">Source X coordinate. Use this parameter to specify cropping from the left edge of the image. Use 0 to display full image.</param>
                /// <param name="ySrc">Source Y coordinate. Use this parameter to specify cropping from the top edge of the image. Use 0 to display full image.</param>
                /// <param name="width">Source width. Use this parameter to specify cropping to the right of the image.</param>
                /// <param name="height">Source height. Use this parameter to specify cropping to the right of the image.</param>
                public void DisplayImage(Picture picture, uint x, uint y, uint xSrc, uint ySrc, uint width, uint height)
                {
                    DisplayImage(picture.MakeBitmap(), x, y, xSrc, ySrc, width, height);
                }

                /// <summary>
                /// Displays a <see cref="T:Gadgeteer.Picture"/> image on the screen.
                /// </summary>
                /// <param name="picture">The <see cref="T:Gadgeteer.Picture"/> to display.</param>
                /// <param name="x">Horizontal position of the left edge of the bitmap on the display.</param>
                /// <param name="y">Vertical position of the top edge of the bitmap on the display.</param>
                public void DisplayImage(Picture picture, uint x, uint y)
                {
                    var bitmap = picture.MakeBitmap();
                    DisplayImage(bitmap, x, y, 0, 0, (uint)bitmap.Width, (uint)bitmap.Height);
                }

                /// <summary>
                ///  Displays an image from a file on the screen.
                /// </summary>
                /// <param name="filePath">The path to the image file.</param>
                /// <param name="imageType">The type of image contained in <paramref name="filePath"/>.</param>
                /// <param name="x">Horizontal position of the left edge of the image on the display.</param>
                /// <param name="y">Vertical position of the top edge of the image on the display.</param>
                /// <param name="xSrc">Source X coordinate. Use this parameter to specify cropping from the left edge of the image. Use 0 to display full image.</param>
                /// <param name="ySrc">Source Y coordinate. Use this parameter to specify cropping from the top edge of the image. Use 0 to display full image.</param>
                /// <param name="width">Source width. Use this parameter to specify cropping to the right of the image. Use bitmap.Width to display full image.</param>
                /// <param name="height">Source height. Use this parameter to specify cropping to the right of the image. Use bitmap.Height to display full image.</param>
                public void DisplayImage(string filePath, Bitmap.BitmapImageType imageType, uint x, uint y, uint xSrc, uint ySrc, uint width, uint height)
                {

                    Bitmap bitmap = LoadImage(filePath, imageType);

                    DisplayImage(bitmap, x, y, xSrc, xSrc, width, height);
                }

                /// <summary>
                ///  Displays an image from a file on the screen.
                /// </summary>
                /// <param name="filePath">The path to the image file.</param>
                /// <param name="imageType">The type of image contained in <paramref name="filePath"/>.</param>
                /// <param name="x">Horizontal position of the left edge of the image on the display.</param>
                /// <param name="y">Vertical position of the top edge of the image on the display.</param>
                public void DisplayImage(string filePath, Bitmap.BitmapImageType imageType, uint x, uint y)
                {
                    Bitmap bitmap = LoadImage(filePath, imageType);

                    DisplayImage(bitmap, x, y, 0, 0, (uint)bitmap.Width, (uint)bitmap.Height);
                }

                private static Bitmap LoadImage(string filePath, Bitmap.BitmapImageType imageType)
                {
                    return new Bitmap(File.ReadAllBytes(filePath), imageType);
                }

            }
        }
    }
}
